import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class Main {

//	public static void main(String[] args) {
//		try {
//			int folds = 10;
//
//			String fileName = "./../../data/leukemia/leukemia.dat";
//			File file = new File(fileName);
//
//			DataBase dataBase = new DataBase(file);
//
//			// dataBase.shuffle();
//
//			ArrayList<File> files = dataBase.balancedDivision(0.67);
//			File trainingFile = files.get(0);
//			File testingFile = files.get(1);
//
//			runTest(trainingFile, testingFile, folds);
//
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//	}
	
	public static void main(String[] args) {
		try {
			int folds = 10;
			float trainingRatio = 0.75f;
			String folderName = "./../../data2";

			runTestInFolder(folderName, trainingRatio, folds);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void runTestInFolder(String folderPath, float trainingRatio, int folds) throws Exception {
		File file = new File(folderPath);

		if (!file.isDirectory()) {
			throw new Exception("Diretório inválido!");
		}

		File[] files = file.listFiles();

		for (File subFolder : files) {
			if (subFolder.isDirectory()) {
				File[] dataBases = subFolder.listFiles(new FilenameFilter() {
					@Override
					public boolean accept(File dir, String name) {
						return name.toLowerCase().endsWith(".dat") || name.toLowerCase().endsWith(".arff");
					}
				});

				for (File fileDataBase : dataBases) {
					DataBase dataBase = new DataBase(fileDataBase);

					ArrayList<File> dividedFiles = dataBase
							.balancedDivision(trainingRatio);
					File trainingFile = dividedFiles.get(0);
					File testingFile = dividedFiles.get(1);

					runTest(trainingFile, testingFile, folds);
				}
			}
		}
	}
	
	public static void runTest(File training, File testing, int folds) throws Exception {
		DataBase trainingBase = new DataBase(training);
		DataBase testingBase = new DataBase(testing);

		/*
		 * Controle de cross validation com mais folds que a menor database
		 * usada em teste
		 */
		folds = Math.min(folds, trainingBase.getInstances());
		folds = Math.min(folds, testingBase.getInstances());

		runTest(trainingBase, testingBase, folds);
	}

	public static void runTest(DataBase dataBaseTrain, DataBase dataBaseTest, int folds) throws Exception {
		System.out.printf("Database: %s\n\n", dataBaseTrain.getName());
		System.out.printf("Instâncias: %d\n\n", dataBaseTrain.getInstances());

		Shrinker shrinker = new Shrinker(dataBaseTrain, folds);

		double delta = shrinker.searchDelta();

		System.out.printf("Delta: %f \n\n", delta);

		HashMap<String, Centroid> shrunkenCentroids = shrinker.shrinkCentroids(shrinker.getNearestCentroidOverall(), delta);

		Set<Integer> discriminantAttributes = shrinker.discriminantAttributes(shrunkenCentroids);

		System.out.printf("Quantidade original de atributos: %d\n", dataBaseTrain.getAttributes().size() - 1);
		System.out.printf("Quantidade shrunken de atributos: %d\n", discriminantAttributes.size());

		System.out.println();

		File shrunkenFile = dataBaseTrain.writeDataBase(dataBaseTrain.getFilePathWithoutExtension() + ".shrunken.arff", discriminantAttributes, true);
		DataBase shrunkenDataBase = new DataBase(shrunkenFile);

		System.out.printf("Erro original (CV-%d): %f\n", folds, Utils.crossValidation(dataBaseTrain, folds));
		System.out.printf("Erro shrunken (CV-%d): %f\n", folds, shrinker.crossValidation(delta));
		System.out.printf("Erro shrunkenDB (CV-%d): %f\n", folds, Utils.crossValidation(shrunkenDataBase, folds));

		System.out.println();

		long tempoInicial, tempoFinal;

		tempoInicial = System.currentTimeMillis();
		new DataBase(dataBaseTrain.getFile());
		tempoFinal = System.currentTimeMillis();
		System.out.printf("Tempo de importação original: %.3f segundos%n", (tempoFinal - tempoInicial) / 1000d);

		tempoInicial = System.currentTimeMillis();
		new DataBase(shrunkenFile);
		tempoFinal = System.currentTimeMillis();
		System.out.printf("Tempo de importação shrunken: %.3f segundos%n", (tempoFinal - tempoInicial) / 1000d);

		System.out.println();

		tempoInicial = System.currentTimeMillis();
		Utils.crossValidation(dataBaseTrain, folds);
		tempoFinal = System.currentTimeMillis();
		System.out.printf("Tempo classficação original (CV-%d): %.3f segundos%n", folds,
				(tempoFinal - tempoInicial) / 1000d);

		tempoInicial = System.currentTimeMillis();
		Utils.crossValidation(shrunkenDataBase, folds);
		tempoFinal = System.currentTimeMillis();
		System.out.printf("Tempo classificação shrunken (CV-%d): %.3f segundos%n", folds,
				(tempoFinal - tempoInicial) / 1000d);

		System.out.println();
		System.out.println();

		// Teste de seleção de características em database de teste desconhecida
		// a partir de atributos discriminantes previamente encontrados

		File shrunkenTestFile = dataBaseTest.writeDataBase(dataBaseTest.getFilePathWithoutExtension()
				+ ".shrunken.arff", discriminantAttributes, true);
		DataBase shrunkenDataBaseTest = new DataBase(shrunkenTestFile);

		System.out.printf("Database: %s\n\n", dataBaseTest.getName());
		System.out.printf("Instâncias: %d\n\n", dataBaseTest.getInstances());

		System.out.printf("Erro original (CV-%d): %f\n", folds, Utils.crossValidation(dataBaseTest, folds));
		System.out.printf("Erro shrunken (CV-%d): %f\n", folds, shrinker.crossValidation(delta));
		System.out.printf("Erro shrunkenDB (CV-%d): %f\n", folds, Utils.crossValidation(shrunkenDataBaseTest, folds));

		System.out.println();
		System.out.println();

		// Teste de classificacao usando como treinamento a base usada para
		// seleção de caracteristicas antes e depois
		NearestCentroidClassifier classifierOriginal = new NearestCentroidClassifier(dataBaseTrain.getSamples(),
				dataBaseTest.getSamples());
		NearestCentroidClassifier classifierShrunken = new NearestCentroidClassifier(shrunkenDataBase.getSamples(),
				shrunkenDataBaseTest.getSamples());
		System.out.println("Teste de classificação");
		System.out.printf("Erro de classificação das amostras de teste: %s\n", classifierOriginal.classifyTestSet());
		System.out.printf("Erro de classificação das amostras de teste shrunken: %s\n", classifierShrunken.classifyTestSet());
		
		
		tempoInicial = System.currentTimeMillis();
		classifierOriginal = new NearestCentroidClassifier(dataBaseTrain.getSamples(), dataBaseTest.getSamples());
		tempoFinal = System.currentTimeMillis();
		System.out.printf("Tempo construção modelo original: %.3f segundos%n", (tempoFinal - tempoInicial) / 1000d);
		
		tempoInicial = System.currentTimeMillis();
		classifierShrunken = new NearestCentroidClassifier(shrunkenDataBase.getSamples(), shrunkenDataBaseTest.getSamples());
		tempoFinal = System.currentTimeMillis();
		System.out.printf("Tempo construção modelo shrunken: %.3f segundos%n", (tempoFinal - tempoInicial) / 1000d);
		
		System.out.println("\n\n\n---------------------------------------------------------------------------------\n\n\n");
	}
}
